/**
 * @Class:yangyl
 * @CreateDate:Jul 11, 2010
 * @Version:
 * @author yangyl
 */
package com.yyl.common.utils;

import com.fasterxml.jackson.annotation.JsonIgnore;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.net.*;
import java.util.*;
import java.util.jar.*;
import javax.persistence.Id;
import org.json.JSONArray;
import org.json.JSONObject;

public final class ReflectionUtils {

    public static Map<String, Object> getPublicFinalStaticValueOfClass(Class<?> clz) {
        Map<String, Object> fvalue = new HashMap();
        Map<String, Field> fmap = getPublicFinalStaticFieldOfClass(clz);
        for (Map.Entry<String, Field> entry : fmap.entrySet()) {
            try {
                fvalue.put(entry.getKey(), entry.getValue().get(null));
            } catch (Throwable ex) {
            }
        }
        return fvalue;
    }

    public static Map<String, Field> getPublicFinalStaticFieldOfClass(Class<?> clz) {
        Map<String, Field> fmap = new HashMap();
        if (clz.equals(Object.class)) {
            return fmap;
        }
        for (Field fld : clz.getDeclaredFields()) {
            try {
                int modifiers = fld.getModifiers();
                if (Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)) {
                    fmap.put(fld.getName(), fld);
                }
            } catch (Throwable ex) {

            }
        }
        return fmap;
    }

    public static Class<?> getGenericClass(Class<?> tp, int index) {
        return (Class<?>) ((ParameterizedType) tp.getGenericSuperclass()).getActualTypeArguments()[index];
    }

    public static String getClassNameFromClass(Class entityClass) {
        return entityClass.getName().replace(entityClass.getPackage().getName(), "").substring(1);
    }

    public static String getClassNameFromClass(String entityClass) throws ClassNotFoundException {
        return getClassNameFromClass(Class.forName(entityClass));
    }

    public static Field getIdFieldFromClass(Class cl) {
        if (cl.equals(Object.class)) {
            return null;
        }
        for (Field f : cl.getDeclaredFields()) {
            if (f.isAnnotationPresent(Id.class)) {
                return f;
            }
        }
        return getIdFieldFromClass(cl.getSuperclass());
    }

    /**
     * 通过反射,获得指定类的父类的泛型参数的实际类型. 如DaoSupport<Buyer>
     *
     * @param clazz clazz 需要反射的类,该类必须继承范型父类
     * @param index 泛型参数所在索引,从0开始.
     * @return 范型参数的实际类型, 如果没有实现ParameterizedType接口，即不支持泛型，所以直接返回
     * <code>Object.class</code>
     */
    @SuppressWarnings("unchecked")
    public static Class getSuperClassGenricType(Class clazz, int index) {

        Type genType = clazz.getGenericSuperclass();// 得到泛型父类

        // 如果没有实现ParameterizedType接口，即不支持泛型，直接返回Object.class
        if (!(genType instanceof ParameterizedType)) {

            return Object.class;
        }

        // 返回表示此类型实际类型参数的Type对象的数组,数组里放的都是对应类型的Class, 如BuyerServiceBean extends
        // DaoSupport<Buyer,Contact>就返回Buyer和Contact类型
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
        if (index >= params.length || index < 0) {

            throw new RuntimeException("你输入的索引"
                    + (index < 0 ? "不能小于0" : "超出了参数的总数"));
        }
        if (!(params[index] instanceof Class)) {

            return Object.class;
        }
        return (Class) params[index];
    }

    /**
     * 通过反射,获得指定类的父类的第一个泛型参数的实际类型. 如DaoSupport<Buyer>
     *
     * @param clazz clazz 需要反射的类,该类必须继承泛型父类
     * @return 泛型参数的实际类型, 如果没有实现ParameterizedType接口，即不支持泛型，所以直接返回
     * <code>Object.class</code>
     */
    @SuppressWarnings("unchecked")
    public static Class getSuperClassGenricType(Class clazz) {

        return getSuperClassGenricType(clazz, 0);
    }

    /**
     * 通过反射,获得方法返回值泛型参数的实际类型. 如: public Map<String, Buyer> getNames(){}
     *
     * @param Method method 方法
     * @param int index 泛型参数所在索引,从0开始.
     * @return 泛型参数的实际类型, 如果没有实现ParameterizedType接口，即不支持泛型，所以直接返回
     * <code>Object.class</code>
     */
    @SuppressWarnings("unchecked")
    public static Class getMethodGenericReturnType(Method method, int index) {

        Type returnType = method.getGenericReturnType();

        if (returnType instanceof ParameterizedType) {

            ParameterizedType type = (ParameterizedType) returnType;
            Type[] typeArguments = type.getActualTypeArguments();

            if (index >= typeArguments.length || index < 0) {

                throw new RuntimeException("你输入的索引"
                        + (index < 0 ? "不能小于0" : "超出了参数的总数"));
            }
            return (Class) typeArguments[index];
        }
        return Object.class;
    }

    /**
     * 通过反射,获得方法返回值第一个泛型参数的实际类型. 如: public Map<String, Buyer> getNames(){}
     *
     * @param Method method 方法
     * @return 泛型参数的实际类型, 如果没有实现ParameterizedType接口，即不支持泛型，所以直接返回
     * <code>Object.class</code>
     */
    @SuppressWarnings("unchecked")
    public static Class getMethodGenericReturnType(Method method) {

        return getMethodGenericReturnType(method, 0);
    }

    /**
     * 通过反射,获得方法输入参数第index个输入参数的所有泛型参数的实际类型. 如: public void add(Map<String,
     * Buyer> maps, List<String> names){}
     *
     * @param Method method 方法
     * @param int index 第几个输入参数
     * @return 输入参数的泛型参数的实际类型集合, 如果没有实现ParameterizedType接口，即不支持泛型，所以直接返回空集合
     */
    @SuppressWarnings("unchecked")
    public static List<Class> getMethodGenericParameterTypes(Method method,
            int index) {

        List<Class> results = new ArrayList<Class>();
        Type[] genericParameterTypes = method.getGenericParameterTypes();

        if (index >= genericParameterTypes.length || index < 0) {

            throw new RuntimeException("你输入的索引"
                    + (index < 0 ? "不能小于0" : "超出了参数的总数"));
        }
        Type genericParameterType = genericParameterTypes[index];

        if (genericParameterType instanceof ParameterizedType) {

            ParameterizedType aType = (ParameterizedType) genericParameterType;
            Type[] parameterArgTypes = aType.getActualTypeArguments();
            for (Type parameterArgType : parameterArgTypes) {
                Class parameterArgClass = (Class) parameterArgType;
                results.add(parameterArgClass);
            }
            return results;
        }
        return results;
    }

    /**
     * 通过反射,获得方法输入参数第一个输入参数的所有泛型参数的实际类型. 如: public void add(Map<String, Buyer>
     * maps, List<String> names){}
     *
     * @param Method method 方法
     * @return 输入参数的泛型参数的实际类型集合, 如果没有实现ParameterizedType接口，即不支持泛型，所以直接返回空集合
     */
    @SuppressWarnings("unchecked")
    public static List<Class> getMethodGenericParameterTypes(Method method) {

        return getMethodGenericParameterTypes(method, 0);
    }

    /**
     * 通过反射,获得Field泛型参数的实际类型. 如: public Map<String, Buyer> names;
     *
     * @param Field field 字段
     * @param int index 泛型参数所在索引,从0开始.
     * @return 泛型参数的实际类型, 如果没有实现ParameterizedType接口，即不支持泛型，所以直接返回
     * <code>Object.class</code>
     */
    @SuppressWarnings("unchecked")
    public static Class getFieldGenericType(Field field, int index) {

        Type genericFieldType = field.getGenericType();

        if (genericFieldType instanceof ParameterizedType) {

            ParameterizedType aType = (ParameterizedType) genericFieldType;
            Type[] fieldArgTypes = aType.getActualTypeArguments();
            if (index >= fieldArgTypes.length || index < 0) {

                throw new RuntimeException("你输入的索引"
                        + (index < 0 ? "不能小于0" : "超出了参数的总数"));
            }
            return (Class) fieldArgTypes[index];
        }
        return Object.class;
    }

    /**
     * 通过反射,获得Field泛型参数的实际类型. 如: public Map<String, Buyer> names;
     *
     * @param Field field 字段
     * @param int index 泛型参数所在索引,从0开始.
     * @return 泛型参数的实际类型, 如果没有实现ParameterizedType接口，即不支持泛型，所以直接返回
     * <code>Object.class</code>
     */
    @SuppressWarnings("unchecked")
    public static Class getFieldGenericType(Field field) {

        return getFieldGenericType(field, 0);
    }

   
    public static Map<String, Method> getAllMethodOfClass(Class<?> clz) {
        Map<String, Method> fmap = new HashMap();
        if (clz.equals(Object.class)) {
            return fmap;
        }
        Method[] methods = clz.getDeclaredMethods();
        for (Method mtd : methods) {
            fmap.put(mtd.getName(), mtd);
        }
        Map<String, Method> superMethods = getAllMethodOfClass(clz.getSuperclass());
        for (Map.Entry<String, Method> sfe : superMethods.entrySet()) {
            if (fmap.containsKey(sfe.getKey()) == false) {
                fmap.put(sfe.getKey(), sfe.getValue());
            }
        }
        return fmap;
    }

    private final static Map<Class<?>, Map<String, Field>> fieldsOfClassMap = new HashMap();

    public static Map<String, Field> getAllFieldOfClass(Class<?> clz) {
        return getAllFieldOfClass(clz, true);
    }

    public static Map<String, Field> getAllFieldOfClass(Class<?> clz, boolean cached) {
        if (cached && fieldsOfClassMap.containsKey(clz)) {
            return fieldsOfClassMap.get(clz);
        } else {

            Map<String, Field> fmap = new HashMap();
            if (clz.equals(Object.class)) {
                return fmap;
            }

            Field[] fields = clz.getDeclaredFields();
            for (Field fld : fields) {
                fmap.put(fld.getName(), fld);
            }

            Map<String, Field> superFields = getAllFieldOfClass(clz.getSuperclass(), false);

            for (Map.Entry<String, Field> sfe : superFields.entrySet()) {
                if (fmap.containsKey(sfe.getKey()) == false) {
                    fmap.put(sfe.getKey(), sfe.getValue());
                }
            }

            fieldsOfClassMap.put(clz, fmap);
            return fmap;
        }
    }

    public static Set<Class<?>> getClasses(Package pack) {

        Set<Class<?>> classes = new LinkedHashSet();
        boolean recursive = true;
        String packageName = pack.getName();
        String packageDirName = packageName.replace('.', '/');
        Enumeration<URL> dirs;
        try {
            dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
            while (dirs.hasMoreElements()) {
                URL url = dirs.nextElement();
                String protocol = url.getProtocol();
                if ("file".equals(protocol)) {
                    String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                    findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
                } else if ("jar".equals(protocol)) {
                    JarFile jar;
                    try {
                        jar = ((JarURLConnection) url.openConnection()).getJarFile();
                        Enumeration<JarEntry> entries = jar.entries();
                        while (entries.hasMoreElements()) {
                            JarEntry entry = entries.nextElement();
                            String name = entry.getName();
                            if (name.charAt(0) == '/') {
                                name = name.substring(1);
                            }
                            if (name.startsWith(packageDirName)) {
                                int idx = name.lastIndexOf('/');
                                if (idx != -1) {
                                    packageName = name.substring(0, idx).replace('/', '.');
                                }
                                if ((idx != -1) || recursive) {
                                    if (name.endsWith(".class") && !entry.isDirectory()) {
                                        String className = name.substring(packageName.length() + 1, name.length() - 6);
                                        try {
                                            classes.add(Class.forName(packageName + '.' + className));
                                        } catch (ClassNotFoundException e) {
                                            e.printStackTrace();
                                        }
                                    }
                                }
                            }
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return classes;
    }

    public static void findAndAddClassesInPackageByFile(String packageName,
            String packagePath, final boolean recursive, Set<Class<?>> classes) {
        File dir = new File(packagePath);
        if (!dir.exists() || !dir.isDirectory()) {
            return;
        }
        File[] dirfiles = dir.listFiles(new FileFilter() {

            public boolean accept(File file) {
                return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
            }
        });
        for (File file : dirfiles) {
            if (file.isDirectory()) {
                findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes);
            } else {
                String className = file.getName().substring(0, file.getName().length() - 6);
                try {
                    classes.add(Class.forName(packageName + '.' + className));
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }

//    public static <T> T getProxiedTargetObject(Object proxy, Class<T> targetClass) throws Exception {
//        if (AopUtils.isJdkDynamicProxy(proxy)) {
//            return (T) ((Advised) proxy).getTargetSource().getTarget();
//        } else {
//            return (T) proxy; // expected to be cglib proxy then, which is simply a specialized class
//        }
//    }
//
//    private static final HashSet<Class<?>> WRAPPER_TYPES = getWrapperTypes();
//
//    private static final Map<Class<?>, Class<?>> WRAPPER_TYPE_MAP = getWrapperTypeMap();
//
//    public static Class<?> getPrimativeWrapperClass(Class<?> original) {
//        if (original.isPrimitive()) {
//            return WRAPPER_TYPE_MAP.get(original);
//        } else {
//            return null;
//        }
//    }

    private static HashMap<Class<?>, Class<?>> getWrapperTypeMap() {
        HashMap<Class<?>, Class<?>> ret = new HashMap();
        ret.put(boolean.class, Boolean.class);
        ret.put(char.class, Character.class);
        ret.put(byte.class, Byte.class);
        ret.put(short.class, Short.class);
        ret.put(int.class, Integer.class);
        ret.put(long.class, Long.class);
        ret.put(float.class, Float.class);
        ret.put(double.class, Double.class);
        ret.put(void.class, Void.class);
        return ret;
    }

    private static HashSet<Class<?>> getWrapperTypes() {
        HashSet<Class<?>> ret = new HashSet();
        ret.add(Boolean.class);
        ret.add(Character.class);
        ret.add(Byte.class);
        ret.add(Short.class);
        ret.add(Integer.class);
        ret.add(Long.class);
        ret.add(Float.class);
        ret.add(Double.class);
        ret.add(Void.class);
        ret.add(CharSequence.class);
        ret.add(String.class);
        return ret;
    }

//    public static boolean isPrimitive(Class<?> cls) {
//        return cls.isPrimitive() || WRAPPER_TYPES.contains(cls);
//    }

    public static <ToType, FromType> ToType 莫林风尚(ToType to, FromType from, String... explict) {
        return copy(to, from, false, explict);
    }

    public static <ToType, FromType> ToType copy(ToType to, FromType from, boolean notNull, String... explict) {
        return copy(to, from, (Class<ToType>) to.getClass(), (Class<FromType>) from.getClass(), notNull, explict);
    }

    /**
     * 这个货是用来把一个前端的对象copy到数据库对象上面，通常用于偷懒滳，哈哈。
     *
     * @param <ToType>
     * @param <FromType>
     * @param to
     * @param from
     * @param toClass
     * @param fromClass
     * @param notNull : 如果为true, 那么只会覆盖那些非空的字段。
     * @param explict 这个是这样的， 你有些字段不想加入copy列表， 跟在最后就好了。aaa
     * @return
     */
    public static <ToType, FromType> ToType copy(ToType to, FromType from, Class<? super ToType> toClass, Class<? super FromType> fromClass, boolean notNull, String... explict) {

        Map<String, Field> fromClassFlds = getAllFieldOfClass(from.getClass());
        Map<String, Field> toClassFlds = getAllFieldOfClass(to.getClass());

        Set<String> lex = null;
        if (explict != null && explict.length != 0) {
            lex = new HashSet(Arrays.asList(explict));
        } else {
            lex = new HashSet();
        }

        for (Map.Entry<String, Field> fldEntry : fromClassFlds.entrySet()) {

            if (lex.isEmpty() || lex.contains(fldEntry.getKey()) == false) {

                if (toClassFlds.containsKey(fldEntry.getKey())) {

                    Field fldFrom = fldEntry.getValue();

                    Field fldTo = toClassFlds.get(fldEntry.getKey());

                    boolean accessableTo = fldTo.isAccessible();
                    boolean accessableFrom = fldFrom.isAccessible();

                    fldFrom.setAccessible(true);
                    fldTo.setAccessible(true);

                    try {
                        if (fldTo.getType().equals(fldFrom.getType())) {
                            Object fromValue = fldFrom.get(from);
                            if (notNull) {
                                if (fromValue != null) {
                                    fldTo.set(to, fldFrom.get(from));
                                }
                            } else {
                                fldTo.set(to, fldFrom.get(from));
                            }
                        }
                    } catch (Throwable ex) {
                        ex.printStackTrace();
                    }

                    fldFrom.setAccessible(accessableFrom);
                    fldTo.setAccessible(accessableTo);

                }
            }
        }
        return to;
    }

    public static List<Class> getAllInterfaces(Class<?> clz) {
        List<Class> lclz = new ArrayList<Class>();
        if (clz != null) {
            Class<?>[] interfaces = clz.getInterfaces();
            for (int i = 0; i < interfaces.length; ++i) {
                lclz.add(interfaces[i]);
            }
        }
        return lclz;
    }

    public static List<Class> getAllSuperClasses(Class<?> clz) {
        List<Class> lclz = new ArrayList<Class>();
        if (clz != null && clz.equals(Object.class) == false) {
            lclz.add(clz.getSuperclass());
            lclz.addAll(getAllSuperClasses(clz.getSuperclass()));
        }
        return lclz;
    }

    public static <Tp> Tp getInstance(Class<Tp> clz) {
        try {
            return clz.newInstance();
        } catch (Exception ex) {
        	ex.printStackTrace();
        }
        return null;
    }

    public static <Tp> Tp cast(Object target, Class<Tp> clz) {
        return (Tp) target;
    }

//    public static Object unwrapProxy(Object proxy) {
//        if (AopUtils.isAopProxy(proxy) && proxy instanceof Advised) {
//            try {
//                Object target = ((Advised) proxy).getTargetSource().getTarget();
//                return target;
//            } catch (Exception e) {
//            }
//        }
//        return proxy;
//    }
//
//    public static Class getOriginalClassWithoutProxy(Object service) {
//        if (AopUtils.isAopProxy(service)) {
//            if (AopUtils.isJdkDynamicProxy(service)) {
//                try {
//                    return ((Advised) service).getTargetSource().getTarget().getClass();
//                } catch (Exception ex) {
//                    return null;
//                }
//            } else if (AopUtils.isCglibProxy(service)) {
//                return service.getClass().getSuperclass();
//            } else {
//                return null;
//            }
//        } else {
//            return service.getClass();
//        }
//    }
//
//    public static String convertGetterToFieldNameIfPossible(String methodName) {
//        if (methodName.toLowerCase().startsWith("get") && methodName.equals("get") == false) {
//            StringBuilder osb = new StringBuilder();
//            osb.append(Character.toLowerCase(methodName.charAt(3)));
//            osb.append(methodName.substring(4));
//            return osb.toString();
//        } else {
//            return methodName;
//        }
//    }
//
//    public static String convertGetterToFieldNameIfPossible(Method method) {
//        String methodName = method.getName();
//        return convertGetterToFieldNameIfPossible(methodName);
//    }
//
//    public static Object getFieldValue(Object target, String fieldName) throws Exception {
//        String methodName = fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
//        Method method = target.getClass().getMethod("get" + methodName);
//        return method.invoke(target);
//    }

}
